package pers.xiaojun.boot.module.infra.service.file;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Sets;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import pers.xiaojun.boot.common.exception.BusinessException;
import pers.xiaojun.boot.common.util.CollectionUtils;
import pers.xiaojun.boot.common.util.FileUtils;
import pers.xiaojun.boot.common.util.HttpUtils;
import pers.xiaojun.boot.file.core.client.FileClient;
import pers.xiaojun.boot.file.core.client.FileClientFactory;
import pers.xiaojun.boot.file.core.pojo.dto.FilePresignedUrlRespDTO;
import pers.xiaojun.boot.module.infra.controller.file.vo.file.FilePresignedUrlRespVO;
import pers.xiaojun.boot.module.infra.controller.file.vo.file.FileQueryReqVO;
import pers.xiaojun.boot.module.infra.controller.file.vo.file.FileSaveReqVO;
import pers.xiaojun.boot.module.infra.dao.entity.FileConfigDO;
import pers.xiaojun.boot.module.infra.dao.entity.FileDO;
import pers.xiaojun.boot.module.infra.dao.mapper.FileMapper;
import pers.xiaojun.boot.mybatis.core.convert.PageConvert;
import pers.xiaojun.boot.mybatis.core.query.LambdaQueryWrapperX;

import java.util.Collection;
import java.util.List;
import java.util.Set;

import static pers.xiaojun.boot.module.infra.constants.BusinessCodeConstants.FILE_NOT_EXISTS;
import static pers.xiaojun.boot.module.infra.constants.BusinessCodeConstants.FILE_URL_NOT_EXISTS;

/**
 * 文件服务层实现
 *
 * @author xiaojun
 * @since 2025-11-13
 */
@Service
@RequiredArgsConstructor
public class FileServiceImpl extends ServiceImpl<FileMapper, FileDO> implements FileService {

    private final FileClientFactory fileClientFactory;
    private final FileConfigService fileConfigService;

    @Override
    public FileDO saveFile(FileSaveReqVO saveVO) {
        // 校验配置
        fileConfigService.validateFileConfigExists(saveVO.getConfigId());

        FileDO fileDO = BeanUtil.toBean(saveVO, FileDO.class);
        this.baseMapper.insert(fileDO);
        return fileDO;
    }

    @Override
    @SneakyThrows
    public String uploadFile(byte[] content, String name, String directory, String type) {
        FileClient client = getMasterFileClient();

        String fileExtension = FileUtils.getFileExtension(name);
        String fileName = IdUtil.fastSimpleUUID() + fileExtension;
        String filePath = HttpUtils.join(directory, fileName);

        String fileUrl = client.upload(content, filePath, type);

        FileDO fileDO = new FileDO()
                .setConfigId(client.getId())
                .setName(name)
                .setPath(filePath)
                .setUrl(fileUrl)
                .setType(type)
                .setSize(content.length);

        this.baseMapper.insert(fileDO);

        return fileUrl;
    }

    @Override
    public void removeFileById(Long fileId) {
        FileClient fileClient = getMasterFileClient();

        FileDO fileDO = validateFileExists(fileId);

        // 删除文件
        fileClient.delete(fileDO.getPath());
        this.baseMapper.deleteById(fileDO.getId());
    }

    @Override
    public void removeFileByIds(Collection<Long> fileIds) {
        List<FileDO> files = this.baseMapper.selectByIds(fileIds);
        if (files.size() != fileIds.size()) {
            Set<Long> foundIds = Sets.difference(
                    Sets.newHashSet(fileIds),
                    CollectionUtils.convertSet(files, FileDO::getId)
            );
            throw new BusinessException(FILE_NOT_EXISTS.getCode(), StrUtil.format("文件Id：{} 不存在", foundIds));
        }

        FileClient fileClient = getMasterFileClient();
        for (FileDO file : files) {
            fileClient.delete(file.getPath());
        }
        this.baseMapper.deleteByIds(fileIds);
    }

    @Override
    public void removeFileByUrl(String url) {
        FileClient fileClient = getMasterFileClient();

        FileDO fileDO = this.baseMapper.selectByUrl(url);
        if (fileDO == null) {
            throw new BusinessException(FILE_URL_NOT_EXISTS);
        }
        // 删除文件
        fileClient.delete(fileDO.getPath());
        this.baseMapper.deleteById(fileDO.getId());
    }

    @Override
    public FilePresignedUrlRespVO getFilePresignedUrl(String directory, String filename) {
        FileClient client = getMasterFileClient();
        String path = HttpUtils.join(directory, filename);

        FilePresignedUrlRespDTO putObjectInfo = client.getPresignedPutObjectUrl(path);
        return new FilePresignedUrlRespVO()
                .setConfigId(client.getId())
                .setUploadUrl(putObjectInfo.getUploadUrl())
                .setUrl(putObjectInfo.getUrl())
                .setPath(path);
    }

    @Override
    public Page<FileDO> listFilesPage(FileQueryReqVO queryVO) {
        return this.baseMapper.selectPage(PageConvert.INSTANCE.toPage(queryVO), new LambdaQueryWrapperX<FileDO>()
                .likeIfPresent(FileDO::getPath, queryVO.getPath())
                .likeIfPresent(FileDO::getType, queryVO.getType())
                .betweenIfPresent(FileDO::getCreateTime, queryVO.getCreateTime())
        );
    }

    /**
     * 校验文件是否存在
     *
     * @param fileId 文件Id
     * @return 文件信息
     */
    private FileDO validateFileExists(Long fileId) {
        FileDO fileDO = this.baseMapper.selectById(fileId);
        if (fileDO == null) {
            throw new BusinessException(FILE_NOT_EXISTS);
        }
        return fileDO;
    }


    /**
     * 获取 Master 客户端
     *
     * @return 文件客户端
     */
    private FileClient getMasterFileClient() {
        FileConfigDO materFileConfig = fileConfigService.getMaterFileConfig();
        return fileClientFactory.getFileClient(materFileConfig.getId());
    }
}
